/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.ole.win32.oleautomation;


private import dwt.dwt;



private import dwt.ole.win32.ole;
private import dwt.ole.win32.oleclientsite;
private import dwt.ole.win32.olecontrolsite;
private import dwt.ole.win32.oleeventsink;
private import dwt.ole.win32.oleframe;
private import dwt.ole.win32.olemisc;
private import dwt.ole.win32.olepropertychangesink;
private import dwt.ole.win32.variant;
private import dwt.internal.ole.win32.com;
private import dwt.internal.ole.win32.comapi;
private import dwt.internal.ole.win32.comtypes;
private import dwt.internal.ole.win32.OAIDL;
private import dwt.internal.win32.os;




private import dwt.util.javatypes;
private import dwt.util.util;
private import dwt.util.vector;
private import dwt.util.hashtable;
private import dwt.util.eventhandler;

/**
 * OleAutomation provides a generic mechanism for accessing functionality that is 
 * specific to a particular ActiveX Control or OLE Document.
 *
 * <p>The OLE Document or ActiveX Control must support the IDispatch interface in order to provide
 * OleAutomation support. The additional functionality provided by the OLE Object is specified in 
 * its IDL file.  The additional methods can either be to get property values (<code>getProperty</code>), 
 * to set property values (<code>setProperty</code>) or to invoke a method (<code>invoke</code> or
 * <code>invokeNoReply</code>).  Arguments are passed around in the form of <code>Variant</code> 
 * objects.
 *
 * <p>Here is a sample IDL fragment:
 *
 * <pre>
 *	interface IMyControl : IDispatch
 *	{
 *		[propget, id(0)] HRESULT maxFileCount([retval, out] int *c);
 *		[propput, id(0)] HRESULT maxFileCount([in] int c);
 *		[id(1)]	HRESULT AddFile([in] BSTR fileName);
 *	};
 * </pre>
 *
 * <p>An example of how to interact with this extended functionality is shown below:
 *
 * <code><pre>
 *	OleAutomation automation = new OleAutomation(myControlSite);
 *
 *	// Look up the ID of the maxFileCount parameter
 *	int[] rgdispid = automation.getIDsOfNames(new String[]{"maxFileCount"});
 *	int maxFileCountID = rgdispid[0];
 *
 *	// Set the property maxFileCount to 100:
 *	if (automation.setProperty(maxFileCountID, new Variant(100))) {
 *		System.out.println("Max File Count was successfully set.");
 *	}
 *
 *	// Get the new value of the maxFileCount parameter:
 *	Variant pVarResult = automation.getProperty(maxFileCountID);
 *	if (pVarResult !is null) {
 *		System.out.println("Max File Count is "+pVarResult.getInt());
 *	}
 *
 *	// Invoke the AddFile method
 *	// Look up the IDs of the AddFile method and its parameter
 *	rgdispid = automation.getIDsOfNames(new String[]{"AddFile", "fileName"}); 
 *	int dispIdMember = rgdispid[0];
 *	int[] rgdispidNamedArgs = new int[] {rgdispid[1]};
 *
 *	// Convert arguments to Variant objects
 *	Variant[] rgvarg = new Variant[1];
 *	String fileName = "C:\\testfile";
 * 	rgvarg[0] = new Variant(fileName);
 *
 *	// Call the method
 *	Variant pVarResult = automation.invoke(dispIdMember, rgvarg, rgdispidNamedArgs);
 *
 *	// Check the return value
 * 	if (pVarResult == null || pVarResult.getInt() != OLE.S_OK){
 * 		System.out.println("Failed to add file "+fileName);
 *	}
 *
 *	automation.dispose();
 *
 * </pre></code>
 */
public class OleAutomation {

	private import std.c.windows.windows;
	private import std.c.windows.com;
	private import Utf = tango.text.convert.Utf ;
	
	
	private IDispatch objIDispatch;
	private char[] exceptionDescription;
	private ITypeInfo objITypeInfo;
	
this(IDispatch idispatch) {
	assert(idispatch);
//	if (idispatch is null) OLE.error(__FILE__, __LINE__, OLE.ERROR_INVALID_INTERFACE_ADDRESS);
	objIDispatch = idispatch;
	objIDispatch.AddRef();
	
	HRESULT result = objIDispatch.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, &objITypeInfo);
	if (result == OLE.S_OK) {
		objITypeInfo.AddRef();
	}
}
/**
 * Creates an OleAutomation object for the specified client.
 *
 * @param clientSite the site for the OLE Document or ActiveX Control whose additional functionality 
 *        you need to access
 *
 * @exception SWTError <ul>
 *		<li>ERROR_INVALID_INTERFACE_ADDRESS when called with an invalid client site
 *	</ul>
 */
 public this(OleClientSite clientSite) {
 	assert(clientSite);
//	if (clientSite is null) OLE.error(__FILE__, __LINE__, OLE.ERROR_INVALID_INTERFACE_ADDRESS);
	objIDispatch = clientSite.getAutomationObject();

	HRESULT result = objIDispatch.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, &objITypeInfo);
	if (result == OLE.S_OK) {
		objITypeInfo.AddRef();
	}
 }
/**
 * Disposes the automation object.
 * <p>
 * This method releases the IDispatch interface on the OLE Document or ActiveX Control.
 * Do not use the OleAutomation object after it has been disposed.
 */
public void dispose() {

	if (objIDispatch !is null){
		objIDispatch.Release();
	}
	objIDispatch = null;
	
	if (objITypeInfo !is null){
		objITypeInfo.Release();
	}
	objITypeInfo = null;

}
package IDispatch getAddress() {	
	return objIDispatch;
}
public char[] getHelpFile(int dispId) {
	if (objITypeInfo is null) return null;
	BSTR file;
	HRESULT rc = objITypeInfo.GetDocumentation(dispId, null, null, null, &file );
	if (rc == OLE.S_OK) {
		char[] str = Converter.WCHARzToStr(file, -1);
		COM.SysFreeString(file);
		return str;		
	}
	return null;	
}
public char[] getDocumentation(int dispId) {
	if (objITypeInfo is null) return null;
	BSTR doc;
	HRESULT rc = objITypeInfo.GetDocumentation(dispId, null, &doc, null, null );
	if (rc == OLE.S_OK) {
		char[] s = Converter.WCHARzToStr(doc, -1);
		COM.SysFreeString(doc);
		return s;
	}
	return null;
}
public OlePropertyDescription getPropertyDescription(int index) {
	if (objITypeInfo is null) return null;
	VARDESC* vardesc;
	HRESULT rc = objITypeInfo.GetVarDesc(index, &vardesc);
	if (rc != OLE.S_OK) return null;
//	VARDESC1* vardesc = new VARDESC1();
//	COM.MoveMemory(vardesc, ppVarDesc[0], VARDESC1.sizeof);
	
	OlePropertyDescription data = new OlePropertyDescription();
	data.id = vardesc.memid;
	data.name = getName(vardesc.memid);
	data.type = vardesc.elemdescVar.tdesc.vt;
	if (data.type == OLE.VT_PTR) {
//		short[] vt = new short[1];
//		COM.MoveMemory(vt, vardesc.elemdescVar.tdesc_union + 4, 2);
		// TODO:
		data.type = vardesc.elemdescVar.tdesc.vt;
	}
	data.flags = vardesc.wVarFlags;
	data.kind = vardesc.varkind;
	data.description = getDocumentation(vardesc.memid);
	data.helpFile = getHelpFile(vardesc.memid);
	
	objITypeInfo.ReleaseVarDesc(vardesc);
	return data;
}
public OleFunctionDescription getFunctionDescription(int index) {
	if (objITypeInfo is null) return null;
	FUNCDESC* funcdesc;
	HRESULT rc = objITypeInfo.GetFuncDesc(index, &funcdesc);
	if (rc != OLE.S_OK) return null;
	
	OleFunctionDescription data = new OleFunctionDescription();
	
	data.id = funcdesc.memid;
	data.optionalArgCount = funcdesc.cParamsOpt;
	data.invokeKind = funcdesc.invkind;
	data.funcKind = funcdesc.funckind;
	data.flags = funcdesc.wFuncFlags;
	data.callingConvention = funcdesc.callconv;
	data.documentation = getDocumentation(funcdesc.memid);
	data.helpFile = getHelpFile(funcdesc.memid);
	
	char[][] names = getNames(funcdesc.memid, funcdesc.cParams + 1);
	if (names.length > 0) {
		data.name = names[0];
	}
	data.args = new OleParameterDescription[funcdesc.cParams];
	for (int i = 0; i < data.args.length; i++) {
		data.args[i] = new OleParameterDescription();
		if (names.length > i + 1) {
			data.args[i].name = names[i + 1];
		}
		short[] vt = new short[1];	
		COM.MoveMemory(vt.ptr, funcdesc.lprgelemdescParam + i * 16 + 4, 2);				
		if (vt[0] == OLE.VT_PTR) {
			int[] pTypedesc = new int[1];
			COM.MoveMemory(pTypedesc.ptr, funcdesc.lprgelemdescParam + i * 16, 4);
			short[] vt2 = new short[1];
			COM.MoveMemory(vt2.ptr, pTypedesc[0] + 4, 2);
			vt[0] = cast(short)(vt2[0] | COM.VT_BYREF);
		}
		data.args[i].type = vt[0];
		short[] wParamFlags = new short[1];
		COM.MoveMemory(wParamFlags.ptr, funcdesc.lprgelemdescParam + i * 16 + 12, 2);
		data.args[i].flags = wParamFlags[0];	
	}
	
	data.returnType = funcdesc.elemdescFunc.tdesc.vt;
	if (data.returnType == OLE.VT_PTR) {
		ushort[] vt = new ushort[1];
		COM.MoveMemory(vt.ptr, funcdesc.elemdescFunc.tdesc.u.lpadesc, 2);
		// TODO:
		data.returnType = vt[0];
	}

	objITypeInfo.ReleaseFuncDesc(funcdesc);
	return data;
}

public TYPEATTR* getTypeInfoAttributes() {
	if (objITypeInfo is null) return null;
	TYPEATTR* ppTypeAttr;
	HRESULT rc = objITypeInfo.GetTypeAttr(&ppTypeAttr);
	if (rc != OLE.S_OK) return null;
	TYPEATTR* typeattr = new TYPEATTR();
	COM.MoveMemory(typeattr, ppTypeAttr, TYPEATTR.sizeof);
	objITypeInfo.ReleaseTypeAttr(ppTypeAttr);
	return typeattr;
}
public char[] getName(int dispId) {
	if (objITypeInfo is null) return null;
	BSTR name;
	HRESULT rc = objITypeInfo.GetDocumentation(dispId, &name, null, null, null );
	if (rc == OLE.S_OK) {
		char[] s = Converter.WCHARzToStr(name, -1);
		COM.SysFreeString(name);
		return s;
	}
	return null;
}

public char[][] getNames(int dispId, int maxSize) {
	if (objITypeInfo is null) return new char[][0];
	BSTR[] names = new BSTR[maxSize];
	uint count;
	HRESULT rc = objITypeInfo.GetNames(dispId, names.ptr, maxSize, &count);
	if (rc == OLE.S_OK) {
		char[][] newNames = new char[][count];
		for(int i=0; i<count; ++i){
			newNames[i] = Converter.WCHARzToStr(names[i], -1);
			COM.SysFreeString(names[i]);
		}
		return newNames;
	}
	return null;
}
/**
 * Returns the positive integer values (IDs) that are associated with the specified names by the
 * IDispatch implementor.  If you are trying to get the names of the parameters in a method, the first 
 * String in the names array must be the name of the method followed by the names of the parameters.
 *
 * @param names an array of names for which you require the identifiers
 *
 * @return positive integer values that are associated with the specified names in the same
 *         order as the names where provided; or null if the names are unknown
 */
public int[] getIDsOfNames(char[][] names) {
	
	int count = names.length;
	wchar*[] wcNames = new wchar*[count];
	for(int i=0; i<count; ++i){
		wcNames[i] = Converter.StrToWCHARz(names[i]);
	}
	int[] rgdispid = new int[count];
	// TODO: NULL GUID ??
	GUID id;
	HRESULT result = objIDispatch.GetIDsOfNames(&id, wcNames.ptr, count, COM.LOCALE_USER_DEFAULT, rgdispid.ptr);
	if (result != COM.S_OK) return null;
	
	return rgdispid;
}
/**
 * Returns a description of the last error encountered.
 *
 * @return a description of the last error encountered
 */
public char[] getLastError() {
	
	return exceptionDescription;

}
/**
 * Returns the value of the property specified by the dispIdMember.
 *
 * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @return the value of the property specified by the dispIdMember or null
 */
public Variant getProperty(int dispIdMember) {
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_PROPERTYGET, null, null, pVarResult);
	return (result == OLE.S_OK) ? pVarResult : null;
}

/**
 * Returns the value of the property specified by the dispIdMember.
 *
 * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *        read only unless the Variant is a By Reference Variant type.
 * 
 * @return the value of the property specified by the dispIdMember or null
 * 
 * @since 2.0
 */
public Variant getProperty(int dispIdMember, Variant[] rgvarg) {
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_PROPERTYGET, rgvarg, null, pVarResult);
	return (result == OLE.S_OK) ? pVarResult : null;
}

/**
 * Returns the value of the property specified by the dispIdMember.
 *
 * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *        read only unless the Variant is a By Reference Variant type.
 * 
 * @param rgdispidNamedArgs an array of identifiers for the arguments specified in rgvarg; the
 *        parameter IDs must be in the same order as their corresponding values;
 *        all arguments must have an identifier - identifiers can be obtained using 
 *        OleAutomation.getIDsOfNames
 * 
 * @return the value of the property specified by the dispIdMember or null
 * 
 * @since 2.0
 */
public Variant getProperty(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs) {
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_PROPERTYGET, rgvarg, rgdispidNamedArgs, pVarResult);
	return (result == OLE.S_OK) ? pVarResult : null;
}

/** 
 * Invokes a method on the OLE Object; the method has no parameters.
 *
 * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @return the result of the method or null if the method failed to give result information
 */
public Variant invoke(int dispIdMember) {
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_METHOD, null, null, pVarResult);
	return (result == COM.S_OK) ? pVarResult : null;
}

/** 
 * Invokes a method on the OLE Object; the method has no optional parameters.
 *
 * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *        read only unless the Variant is a By Reference Variant type.
 *
 * @return the result of the method or null if the method failed to give result information
 */
public Variant invoke(int dispIdMember, Variant[] rgvarg) {
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, null, pVarResult);
	return (result == COM.S_OK) ? pVarResult : null;
}

/** 
 * Invokes a method on the OLE Object; the method has optional parameters.  It is not
 * neccessary to specify all the optional parameters, only include the parameters for which
 * you are providing values.
 *
 * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *        read only unless the Variant is a By Reference Variant type.
 *
 * @param rgdispidNamedArgs an array of identifiers for the arguments specified in rgvarg; the
 *        parameter IDs must be in the same order as their corresponding values;
 *        all arguments must have an identifier - identifiers can be obtained using 
 *        OleAutomation.getIDsOfNames
 *
 * @return the result of the method or null if the method failed to give result information
 */
public Variant invoke(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs) {
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, rgdispidNamedArgs, pVarResult);
	return (result == COM.S_OK) ? pVarResult : null;
}

private int invoke(int dispIdMember, ushort wFlags, Variant[] rgvarg, int[] rgdispidNamedArgs, Variant pVarResult) {
	assert(objIDispatch);
	
	// get the IDispatch interface for the control
	if (objIDispatch is null) return COM.E_FAIL;
	
	// create a DISPPARAMS structure for the input parameters
	DISPPARAMS pDispParams;
	
	// store arguments in rgvarg	
	if (rgvarg !is null && rgvarg.length > 0) {
		VARIANT[] tempArgs = new VARIANT[rgvarg.length];
		for (int i = 0; i < rgvarg.length ; ++i) {
			rgvarg[i].getData(&tempArgs[i]);
		}
		// the reverse sequency
		tempArgs.reverse; 
		pDispParams.cArgs = tempArgs.length;
		pDispParams.rgvarg = tempArgs.ptr;
	}

	// if arguments have ids, store the ids in rgdispidNamedArgs
	if (rgdispidNamedArgs !is null && rgdispidNamedArgs.length > 0) {
		DISPID[] tempArgs = rgdispidNamedArgs.dup;
		// the reverse sequency
		tempArgs.reverse;
		pDispParams.cNamedArgs = tempArgs.length;
		pDispParams.rgdispidNamedArgs = tempArgs.ptr;
	}

	// invoke the method
	EXCEPINFO excepInfo;
	uint pArgErr;
	VARIANT* pVarResultAddress = null;
	if (pVarResult !is null)	
		pVarResultAddress = new VARIANT();
	
	GUID id;	// IID_NULL
	/*
	     HRESULT Invoke(
                [in] DISPID dispIdMember,
                [in] REFIID riid, 	
                [in] LCID lcid,
                [in] WORD wFlags,
                [in, out] DISPPARAMS * pDispParams,
                [out] VARIANT * pVarResult,
                [out] EXCEPINFO * pExcepInfo,
                [out] UINT * puArgErr
            ); 
	 */
	HRESULT result = objIDispatch.Invoke(dispIdMember, &id, COM.LOCALE_USER_DEFAULT, wFlags, &pDispParams, pVarResultAddress, &excepInfo, &pArgErr);

	if (pVarResultAddress !is null){
		pVarResult.setData(pVarResultAddress);
		COM.VariantClear(pVarResultAddress);
	}
	
	// free the Dispparams resources
	if (pDispParams.rgvarg !is null) {		
		for (int i = 0, length = rgvarg.length; i < length; i++){
			COM.VariantClear(&pDispParams.rgvarg[i]);
		}
		pDispParams.rgvarg = null;
	}
	pDispParams.rgdispidNamedArgs = null;

	// save error string and cleanup EXCEPINFO
	manageExcepinfo(result, &excepInfo);
		
	return result;
}
/** 
 * Invokes a method on the OLE Object; the method has no parameters.  In the early days of OLE, 
 * the IDispatch interface was not well defined and some applications (mainly Word) did not support 
 * a return value.  For these applications, call this method instead of calling
 * <code>public void invoke(int dispIdMember)</code>.
 *
 * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @exception SWTError <ul>
 *		<li>ERROR_ACTION_NOT_PERFORMED when method invocation fails
 *	</ul>
 */
public void invokeNoReply(int dispIdMember) {
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_METHOD, null, null, null);
	if (result != COM.S_OK)
		OLE.error(__FILE__, __LINE__, OLE.ERROR_ACTION_NOT_PERFORMED, result);
}
/** 
 * Invokes a method on the OLE Object; the method has no optional parameters.  In the early days of OLE, 
 * the IDispatch interface was not well defined and some applications (mainly Word) did not support 
 * a return value.  For these applications, call this method instead of calling
 * <code>public void invoke(int dispIdMember, Variant[] rgvarg)</code>.
 *
 * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *        read only unless the Variant is a By Reference Variant type.
 *
 * @exception SWTError <ul>
 *		<li>ERROR_ACTION_NOT_PERFORMED when method invocation fails
 *	</ul>
 */
public void invokeNoReply(int dispIdMember, Variant[] rgvarg) {
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, null, null);
	if (result != COM.S_OK)
		OLE.error(__FILE__, __LINE__, OLE.ERROR_ACTION_NOT_PERFORMED, result);
}
/** 
 * Invokes a method on the OLE Object; the method has optional parameters.  It is not
 * neccessary to specify all the optional parameters, only include the parameters for which
 * you are providing values.  In the early days of OLE, the IDispatch interface was not well 
 * defined and some applications (mainly Word) did not support a return value.  For these 
 * applications, call this method instead of calling
 * <code>public void invoke(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs)</code>.
 *
 * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
 *        value for the ID can be obtained using OleAutomation.getIDsOfNames
 *
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *        read only unless the Variant is a By Reference Variant type.
 *
 * @param rgdispidNamedArgs an array of identifiers for the arguments specified in rgvarg; the
 *        parameter IDs must be in the same order as their corresponding values;
 *        all arguments must have an identifier - identifiers can be obtained using 
 *        OleAutomation.getIDsOfNames
 *
 * @exception SWTError <ul>
 *		<li>ERROR_ACTION_NOT_PERFORMED when method invocation fails
 *	</ul>
 */
public void invokeNoReply(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs) {
	HRESULT result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, rgdispidNamedArgs, null);
	if (result != COM.S_OK)
		OLE.error(__FILE__, __LINE__, OLE.ERROR_ACTION_NOT_PERFORMED, result);
}
private void manageExcepinfo(int hResult, EXCEPINFO* excepInfo) {

	if (hResult == COM.S_OK){
		exceptionDescription = ("No Error"); //$NON-NLS-1$
		return;
	}

	// extract exception info
	if (hResult == COM.DISP_E_EXCEPTION) {
		if (excepInfo.bstrDescription !is null){
			exceptionDescription = Converter.WCHARzToStr(excepInfo.bstrDescription);
		} else {
			exceptionDescription = ("OLE Automation Error Exception "); //$NON-NLS-1$
			if (excepInfo.wCode != 0){
				exceptionDescription ~= "code = ";
				exceptionDescription ~= cast(int)(excepInfo.wCode); //$NON-NLS-1$
			} else if (excepInfo.scode != 0){
				exceptionDescription ~= "code = ";
				exceptionDescription ~= (excepInfo.scode); //$NON-NLS-1$
			}
		}
	} else {
		exceptionDescription = ("OLE Automation Error HResult : ") ~ Converter.toHex(hResult); //$NON-NLS-1$
	}

	// cleanup EXCEPINFO struct
	if (excepInfo.bstrDescription !is null)
		COM.SysFreeString(excepInfo.bstrDescription);
	if (excepInfo.bstrHelpFile !is null)
		COM.SysFreeString(excepInfo.bstrHelpFile);
	if (excepInfo.bstrSource !is null)
		COM.SysFreeString(excepInfo.bstrSource);
}
/**
 * Sets the property specified by the dispIdMember to a new value.
 *
 * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
 *                     value for the ID can be obtained using OleAutomation.getIDsOfNames
 * @param rgvarg the new value of the property
 *
 * @return true if the operation was successful
 */
public boolean setProperty(int dispIdMember, Variant rgvarg) {
	
	Variant[] rgvarg2 = new Variant[1];  
	rgvarg2[0] = rgvarg;
	int[] rgdispidNamedArgs;
	rgdispidNamedArgs ~= COM.DISPID_PROPERTYPUT;
	ushort dwFlags = COM.DISPATCH_PROPERTYPUT;
	if ((rgvarg.getType() & COM.VT_BYREF) == COM.VT_BYREF)
		dwFlags = COM.DISPATCH_PROPERTYPUTREF;
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, dwFlags, rgvarg2, rgdispidNamedArgs, pVarResult);
	return (result == COM.S_OK);
}
/**
 * Sets the property specified by the dispIdMember to a new value.
 *
 * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
 *                     value for the ID can be obtained using OleAutomation.getIDsOfNames
 * @param rgvarg an array of arguments for the method.  All arguments are considered to be
 *                     read only unless the Variant is a By Reference Variant type.
 *
 * @return true if the operation was successful
 *
 * @since 2.0
 */
public boolean setProperty(int dispIdMember, Variant[] rgvarg) {
	int[] rgdispidNamedArgs;
	rgdispidNamedArgs ~= COM.DISPID_PROPERTYPUT;
	ushort dwFlags = COM.DISPATCH_PROPERTYPUT;
	for (int i = 0; i < rgvarg.length; i++) {
		if ((rgvarg[i].getType() & COM.VT_BYREF) == COM.VT_BYREF)
		dwFlags = COM.DISPATCH_PROPERTYPUTREF;
	}
	Variant pVarResult = new Variant();
	HRESULT result = invoke(dispIdMember, dwFlags, rgvarg, rgdispidNamedArgs, pVarResult);
	return (result == COM.S_OK);
}
}
